#ifndef VEC3_CLASS_H
#define VEC3_CLASS_H

#define VEC3_BINARY_COMPONENT_OPERATOR(_oper_) \
   vec3<element_type> operator _oper_ (const_reference v) const\
   { \
      vec3<element_type> a; \
      a.x = x _oper_ v.x; \
      a.y = y _oper_ v.y; \
      a.z = z _oper_ v.z; \
      return a; \
   }

#define VEC3_BINARY_BROADCAST_OPERATOR(_oper_) \
   template <typename FT> \
   vec3<element_type> operator _oper_ (FT fValue) const \
   { \
      vec3<element_type> a; \
      a.x = x _oper_ fValue; \
      a.y = y _oper_ fValue; \
      a.z = z _oper_ fValue; \
      return a; \
   }

#define VEC3_COMPONENT_OPERATOR(_oper_) \
   reference operator _oper_ (const_reference v) \
   { \
      if (static_cast<void *>(this) != static_cast<const void *>(&v)) \
      { \
         x _oper_ v.x; \
         y _oper_ v.y; \
         z _oper_ v.z; \
      } \
      return *this; \
   }

#define VEC3_BROADCAST_OPERATOR(_oper_) \
   template <typename FT> \
   reference operator _oper_ (FT fValue) \
   { \
      x _oper_ fValue; \
      y _oper_ fValue; \
      z _oper_ fValue; \
      return *this; \
   }

template <typename T>
struct vec3
{
   typedef vec3 &reference;
   typedef vec3 const &const_reference;
   typedef T element_type;

   union
   {
      element_type v[3];
      struct
      {
         element_type x;
         element_type y;
         element_type z;
      };
   };

   vec3(void) { }
   ~vec3(void) { }
   vec3(element_type vx, element_type vy, element_type vz) : x(vx), y(vy), z(vz) { }

   template <typename VT>
   vec3(const vec3<VT> &v) : x(v.x), y(v.y), z(v.z) { }

   VEC3_BINARY_COMPONENT_OPERATOR(+);
   VEC3_BINARY_COMPONENT_OPERATOR(-);
   VEC3_BINARY_COMPONENT_OPERATOR(*);
   VEC3_BINARY_COMPONENT_OPERATOR(/);

   VEC3_BINARY_BROADCAST_OPERATOR(+);
   VEC3_BINARY_BROADCAST_OPERATOR(-);
   VEC3_BINARY_BROADCAST_OPERATOR(*);
   VEC3_BINARY_BROADCAST_OPERATOR(/);

   VEC3_COMPONENT_OPERATOR(=);
   VEC3_COMPONENT_OPERATOR(+=);
   VEC3_COMPONENT_OPERATOR(-=);
   VEC3_COMPONENT_OPERATOR(*=);
   VEC3_COMPONENT_OPERATOR(/=);

   VEC3_BROADCAST_OPERATOR(=);
   VEC3_BROADCAST_OPERATOR(+=);
   VEC3_BROADCAST_OPERATOR(-=);
   VEC3_BROADCAST_OPERATOR(*=);
   VEC3_BROADCAST_OPERATOR(/=);

   reference make_zero(void)
   {
      x = y = z = 0;
      return *this;
   }

   reference normalize(void)
   {
      element_type rsqrt = 1 / magnitude();
      x *= rsqrt;
      y *= rsqrt;
      z *= rsqrt;
      return *this;
   }

   reference negate(void)
   {
      x = -x;
      y = -y;
      z = -z;
      return *this;
   }

   element_type magnitude(void)
   {
      element_type fMagnitude;
      fMagnitude = (T)sqrt(x*x + y*y + z*z);
      return fMagnitude;
   }

   inline friend vec3<T> interpolate(const_reference vectorA, const_reference vectorB, float lamda)
   {
       return vectorA + ((vectorB - vectorA) * lamda);
   }

   element_type *array(void) { return this->v; }
   element_type const *array(void) const { return this->v; }
};


typedef vec3<float> vec3f;
typedef vec3<double> vec3d;
typedef vec3f float3;
typedef vec3d double3;

#endif
